package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

// 创建int变量并发布为公共变量, 在 /debug/vars 上以JSON格式公布, 通常用于计数
var helloRequest = expvar.NewInt("hello-requests")

// flag定义项目
var webroot = flag.String("root", "/home/www", "项目目录路径")

// 计数器
type Counter struct {
	n int
}

func (c *Counter) String() string {
	return fmt.Sprintf("%d", c.n)
}

// http.Handler: get - n+1; post - n+request.body
func (ctr *Counter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		ctr.n++
	case "POST":
		buf := new(bytes.Buffer)
		io.Copy(buf, r.Body)
		body := buf.String()
		if n, err := strconv.Atoi(body); err != nil {
			fmt.Fprintf(w, "Bad Ruquest: %v\nBody: [%v]\n", err, body)
		} else {
			ctr.n += n
		}

	}
	fmt.Fprintf(w, "Counter = %d\n", ctr.n)
}

// 计数通道
type chInt chan int

// 实现http.Handler
func (ch chInt) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintf("Channel Send #%d\n", <-ch))
}

func CreateChanInt() chInt {
	ch := make(chInt)
	go func() {
		for ix := 0; ; ix++ {
			ch <- ix
		}
	}()

	return ch
}

func setContentType(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
}

func main() {
	// 打印url, 返回404
	http.Handle("/", http.HandlerFunc(Logger))
	// helleRequest计数
	http.Handle("/go/hello", http.HandlerFunc(helloServer))
	// Counter计数
	ctr := new(Counter)
	expvar.Publish("counter", ctr)
	http.Handle("/counter", ctr)
	// 输出指定目录文件
	http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot))))
	// 输出所有Flag
	http.Handle("/flags", http.HandlerFunc(flagServer))
	// 输出所有参数
	http.Handle("/args", http.HandlerFunc(argsServer))
	// 输出通道计数
	http.Handle("/chan", CreateChanInt())
	// 输出系统当前时间(调用linux命令)
	http.Handle("/date", http.HandlerFunc(dateServer))

	if err := http.ListenAndServe(":9999", nil); err != nil {
		log.Panicln("ListenAndServe:", err)
	}
}

func Logger(w http.ResponseWriter, r *http.Request) {
	log.Print(r.URL.String())
	w.WriteHeader(404)
	w.Write([]byte("Oops!"))
}

func helloServer(w http.ResponseWriter, r *http.Request) {
	helloRequest.Add(1)
	io.WriteString(w, "Hello, Golang Learner!")
}

func flagServer(w http.ResponseWriter, r *http.Request) {
	setContentType(w)
	fmt.Fprintf(w, "Flags:\n")
	flag.VisitAll(func(fg *flag.Flag) {
		if fg.Value.String() != fg.DefValue {
			fmt.Fprintf(w, "%s = %s [Default = %s]\n", fg.Name, fg.Value, fg.DefValue)
		} else {
			fmt.Fprintf(w, "%s = %s\n", fg.Name, fg.Value.String())
		}
	})
}

func argsServer(w http.ResponseWriter, r *http.Request) {
	for _, arg := range os.Args {
		fmt.Fprint(w, arg, " ")
	}
}

func dateServer(w http.ResponseWriter, r *http.Request) {
	setContentType(w)
	osread, oswrite, err := os.Pipe()
	if err != nil {
		fmt.Fprintf(w, "Pipe: %s\n", err)
		return
	}
	defer osread.Close()
	osprc, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, oswrite, oswrite}})
	oswrite.Close()
	if err != nil {
		fmt.Fprintf(w, "Fork/Exec: %s\n", err)
		return
	}
	defer osprc.Release()
	io.Copy(w, osread)
	wait, err := osprc.Wait()
	if err != nil {
		fmt.Fprintf(w, "Wait: %s\n", err)
		return
	}
	if !wait.Exited() {
		fmt.Fprintf(w, "Date: %v\n", wait)
		return
	}
}
